/**
 * 图片压缩
 * @author gzkemays
 */

export default class ImageCompress {
  // 大小转换
  byteToKbSize = (byte) => byte / 1024;
  byteToMbSize = (byte) => byte / 1024 / 1024;
  kbToByteSize = (kb) => kb * 1024;
  mbToByteSize = (mb) => mb * 1024 * 1024;

  /**
   * 获取文件的 object url
   * @param file
   */
  getObjectUrl(file) {
    let url = '';
    if (window.URL !== undefined) { // mozilla(firefox)
      url = window.URL.createObjectURL(file);
    } else if (window.webkitURL !== undefined) { // webkit or chrome
      url = window.webkitURL.createObjectURL(file);
    }
    return url;
  }

  /**
   * 图片转 canvas
   */
  imgToCanvas(img) {
    return new Promise((resolve, reject) => {
      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');
      const imgWidth = img.width;
      const imgHeight = img.height;
      canvas.width = imgWidth;
      canvas.height = imgHeight;
      // 尺寸与原图保持一致，因此等高等宽
      if (context) {
        context.clearRect(0, 0, imgWidth, imgHeight);
        context.drawImage(img, 0, 0, imgWidth, imgHeight);
        resolve(canvas);
      }
      reject('error');
    });
  }

  /**
   * canvas 转文件
   */
  canvasToFile(canvas, type, encoderOptions) {
    return new Promise((resolve) => {
      canvas.toBlob(blob => resolve(blob), type, encoderOptions);
    });
  }

  /**
   * 文件转图片
   */
  fileToImage(blob) {
    return new Promise(resolve => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.src = this.getObjectUrl(blob);
    })
  }

  /**
   * 压缩图片
   */
  async compress(originFile, limitedSize) {
    // 计算文件大小 单位为kb
    const originSize = originFile.size / 1024;
    // 若小于 limitedSize，则不需要压缩，直接返回
    if (originSize <= limitedSize) {
      return {file: originFile, msg: '体积小于等于期望值，无需压缩！'};
    }
    // 将获取到的文件恢复成图片
    const img = await this.fileToImage(originFile);
    // 使用此图片生成需要的canvas
    const canvas = await this.imgToCanvas(img);
    // 为规避js精度问题，将 encoderOptions 乘 100 为整数，初始最大 size 为 MAX_SAFE_INTEGER
    const maxQualitySize = {encoderOptions: 100, size: Number.MAX_SAFE_INTEGER};
    // 初始最小size为0
    const minQualitySize = {encoderOptions: 0, size: 0};
    // 初始质量参数
    let encoderOptions = 100,
      // 压缩次数
      count = 0,
      // 出错信息
      errorMsg = '',
      // 压缩后的文件Blob
      compressBlob: any = null;

    // 压缩思路，用二分法找最佳的压缩点 最多尝试8次即可覆盖全部可能
    while (count < 8) {
      compressBlob = await this.canvasToFile(canvas, 'image/jpeg', encoderOptions / 100);
      const compressSize = compressBlob.size / 1024;
      count++;
      // 压缩后的体积与期望值相等
      if (compressSize === limitedSize) {
        break;
      } else if (compressSize > limitedSize) {
        // 压缩后的体积比期望值大
        maxQualitySize.encoderOptions = encoderOptions;
        maxQualitySize.size = compressSize;
      } else {
        // 压缩后的体积比期望值小
        minQualitySize.encoderOptions = encoderOptions;
        minQualitySize.size = compressSize;
      }
      encoderOptions = (maxQualitySize.encoderOptions + minQualitySize.encoderOptions) >> 1;
      // console.log('minQualitySize = ', minQualitySize.size);
      if (maxQualitySize.encoderOptions - minQualitySize.encoderOptions < 2) {
        if (!minQualitySize.size && encoderOptions) {
          encoderOptions = minQualitySize.encoderOptions;
        } else if (!minQualitySize.size && !encoderOptions) {
          errorMsg = '压缩失败，无法压缩到指定大小';
          break;
        } else if (minQualitySize.size > limitedSize) {
          errorMsg = '压缩失败，无法压缩到指定大小';
          break;
        } else {
          // 压缩完成
          encoderOptions = minQualitySize.encoderOptions;
          compressBlob = await this.canvasToFile(canvas, 'image/jpeg', encoderOptions / 100);
          break;
        }
      }
    }
    // 压缩失败，则返回原始图片的信息
    if (errorMsg) {
      return {
        msg: errorMsg,
        file: originFile,
      };
    }
    // 压缩成功生成文件
    const compressedFile = new File([compressBlob], originFile.name, {
      type: 'image/jpeg',
    });
    return {file: compressedFile, compressBlob, msg: '压缩成功！'};
  }
}